#include "data_scene.h"

#include <future>
#include <iostream>
#include <mutex>

#include "data_scene_impl.h"

namespace autodrive {
namespace perception {

DataScene::DataScene() : data_scene_impl_ptr_{nullptr} {}
DataScene::~DataScene() {}

void DataScene::Init(std::filesystem::path const &path) noexcept(false) {
  if (data_scene_impl_ptr_ == nullptr) {
    data_scene_impl_ptr_ = std::make_shared<DataSceneImpl>();
  }

  try {
    data_scene_impl_ptr_->Init(path);
  } catch (std::exception const &e) {
    throw std::runtime_error(path.string() + " init error, " + e.what());
  }
}

bool DataScene::IsInit() const noexcept {
  bool isInit = false;
  if (data_scene_impl_ptr_ != nullptr) {
    isInit = data_scene_impl_ptr_->IsInit();
  }
  return isInit;
}

void DataScene::LoadDataFrame(LoadDataFrameSettings const &settings,
                              std::string const &dataFrameKey,
                              DataFrame &dataFrame) noexcept(false) {
  if (!IsInit()) {
    throw std::runtime_error("data scene does not init");
  }

  auto loadCameraFunc{[this, &settings, &dataFrameKey, &dataFrame]() {
    data_scene_impl_ptr_->LoadCameraData(settings.cameras, dataFrameKey,
                                         dataFrame.camera_data);
  }};
  std::shared_future<void> f{std::async(std::launch::async, loadCameraFunc)};

  data_scene_impl_ptr_->LoadLidarData(settings.lidars, dataFrameKey,
                                      dataFrame.lidar_data);

  data_scene_impl_ptr_->LoadRadarData(settings.radars, dataFrameKey,
                                      dataFrame.radar_data);

  data_scene_impl_ptr_->LoadEgoMotionData(dataFrameKey, dataFrame.ego_data);

  // sync
  f.get();

  dataFrame.timestamp = std::strtoull(dataFrameKey.c_str(), 0, 0);
}

DataScene::DataFrameArrayPtr<std::string> DataScene::DataFrameKeys()
    const noexcept {
  DataScene::DataFrameArrayPtr<std::string> keysPtr{nullptr};
  if (data_scene_impl_ptr_ != nullptr && data_scene_impl_ptr_->IsInit()) {
    keysPtr = data_scene_impl_ptr_->DataframeKeys();
  }
  return keysPtr;
}

DataScene::CalibDataArrayPtr<DataScene::MatrixData> DataScene::CalibData()
    const noexcept {
  DataScene::CalibDataArrayPtr<MatrixData> calibDataPtr{nullptr};
  if (data_scene_impl_ptr_ != nullptr && data_scene_impl_ptr_->IsInit()) {
    calibDataPtr = data_scene_impl_ptr_->CalibData();
  }
  return calibDataPtr;
}

DataScene::MetaDataInfo DataScene::MetaData() const noexcept {
  DataScene::MetaDataInfo info;
  if (data_scene_impl_ptr_ != nullptr && data_scene_impl_ptr_->IsInit()) {
    info = data_scene_impl_ptr_->MetaData();
  }
  return info;
}

std::filesystem::path const &DataScene::Path() const noexcept(false) {
  if (data_scene_impl_ptr_ == nullptr) {
    throw std::runtime_error("data_scene_impl_ptr_ is nullptr");
  }
  return data_scene_impl_ptr_->Path();
}

}  // namespace perception
}  // namespace autodrive